/**
 * hash.js
 *
 * Copyright (C) 2012 Emmanuel Garcia
 * MIT Licensed
 *
 * ****************************************
 *
 * Hash.pushState(true);
 *
 * Hash.on('/page/([0-9]+)$',
 *	{yep: function(path, parts) { }, nop: function() { }},
 *	'Page $1');
 *
 * Hash.go('/page/1');
 **/

(function() {

    'use strict';

    var hashes = {},
    regexp = {},
    history = [],
    freq = 100,
    num = 0,
    pushState = false,
    timer = null,
    currentUrl = null,

    freeze = function(obj) {
        if (Object.freeze) return Object.freeze(obj);
        return obj;
    },

    getHashParts = function() {
        return window.location.href.split('#');
    },

    startTimer = function() {

        if (!timer) timer = setInterval(function() {
            if (num > 0 && currentUrl != window.location.href) {
                currentUrl = window.location.href;
                window.Hash.check();
            }
        },
        freq);

    },

    stopTimer = function() {

        if (timer) {
            clearInterval(timer);
            timer = null;
        }

    };

    window.Hash = freeze({

        pushState: function(yes) {
            if (window.history && window.history.pushState) pushState = yes;
            return this;
        },

        fragment: function() {
            var hash = getHashParts();
            return (pushState) ? window.location.pathname + ((hash[1]) ? '#' + hash[1] : '') : hash[1] || '';
        },

        get: function(path, params) {
            var p, fragment = '',
            parameters = [];
            for (p in params) {
                if (!Object.prototype.hasOwnProperty(p)) continue;
                parameters.push(encodeURIComponent(p) + '=' + encodeURIComponent(params[p]));
            }
            if (parameters.length > 0) parameters = '?' + parameters.join('&');
            return (pushState) ? path + parameters: getHashParts()[0] + '#' + path + parameters;
        },

        go: function(hash, params) {
            if (this.fragment() != hash) {
                var to = this.get(hash, params);
                if (pushState) window.history.pushState(null, document.title, to);
                else window.location.href = to;
            }
            return this;
        },

        update: function() {
            currentUrl = window.location.href;
            return this;
        },

        on: function(hash, callback, title) {
            if (!hashes[hash]) hashes[hash] = {
                title: title,
                listeners: []
            };

            hashes[hash].listeners.push(callback);
            num++;
            startTimer();
            return this;
        },

        check: function() {
            var i, hash, parts, fragment = this.fragment();
            for (hash in hashes) {
                if (!Object.prototype.hasOwnProperty.call(hashes, hash)) continue;
                hashes[hash].regexp = hashes[hash].regexp || new RegExp(hash);
                if ((parts = hashes[hash].regexp.exec(fragment))) {
                    if (hashes[hash].title) document.title = hashes[hash].title;
                    for (i = 0; i < hashes[hash].listeners.length; i++) if (hashes[hash].listeners[i].yep) hashes[hash].listeners[i].yep(fragment, parts);
                } else {
                    for (i = 0; i < hashes[hash].listeners.length; i++) if (hashes[hash].listeners[i].nop) hashes[hash].listeners[i].nop(fragment);
                }
            }
            return this;
        }
    });
})();